#ifndef DISPATCH_PROCESS_H
#define DISPATCH_PROCESS_H

#include <stdint.h>

namespace dispatch {
class QueuePrivate;
class Timer;

/**
 * @brief The Object class
 * @note 所有参与调度或者部分数据类的父类
 *  类中定义virtual函数与否，影响C++的语法规则和自身的编码规则
 *  为了符合C++编码规范，定义虚函数，那么子类继承Object必须处于第一继承位置，否则必然错误
 */
class Object
{
public:
    Object() {}

    //! @warning 此处添加虚析构函数，那么所有子类继承Object时候必须添加到第一继承位置
    //!  eg: class a : public Object, public b {}; 正确
    //!  eg: class a : public b, public Object {}; 错误
    //! 注销虚拟析构函数，就会导致 enqueueAutofree 不能使用，内部有delete操作会导致内存释放失败
#ifndef USE_DESTRUC_OBJ
     virtual ~Object() {}
#endif // USE_DESTRUC_OBJ

    //! 函数回调
    typedef void (Object::*Callback1) ();
};

//! 强制类型转换，把子类中的成员函数回调强制转换成Callback1
//! 用以防止编译器报错，类似宏：
//!     #define DISPATCH_FUNC(fn)  ((dispatch::Object::Callback1) fn)
template <typename Func1> Object::Callback1 FUNC(Func1 fn)
{
    return reinterpret_cast<Object::Callback1>(fn);
}

/**
 * @brief The Queue class
 * @details 调度队列允许其他线程在目标线程中运行回调。
 *  目标线程是调用queueProcess()的线程。
 * 使用析沟函数释放调度队列。在销毁时，队列必须为空。
 * 保证这一点最简单的方法是终止所有潜在的发送者, 然后调用run()函数，
 *  例如，使目标线程退出，然后pthread_join()目标线程，最后销毁队列。
 * 另一种方法是在终止所有潜在的发送者之后调用queueProcess()，然后销毁队列。
 *
 * 当前queue的写法，借鉴于 mpv核心逻辑处理算法
 */
class Queue : public Object
{
public:
    Queue();
    ~Queue();

    /**
     * @brief getNeedWait
     * @return 获取条件等待的时间（微妙：us），是一个相对时间
     * @note 获取定时器列表中需要等待的时间
     *  简而言之就是禁用了queueProcess中的超时等待，即传值为0
     *  而是使用外部的超时等待如：select/poll/等其他可以等待的函数
     *  当使用上述的等待函数时，可以把当前返回值，转换为它的超时等待时间。
     */
    int64_t getNeedWait();

    /**
     * @brief setWakeupFn
     * @param wakeup_fn [in] 回调函数
     * @param ctx [in] 传递给回调函数的参数
     * @note 设置一个应该被调用的自定义函数，以保证目标线程被唤醒。这是用于需要阻塞非pthread原语的代码，例如select()。
     *  例如，在select()的情况下，wakeup_fn可以将一个字节写入“wakeup”管道以解除select()阻塞。
     * 当有新的分派项时，从分派队列调用wakeup_fn，然后目标线程应尽快进入queue_process()。
     * 注意，wakeup_fn是在没有锁的情况下调用的，所以您可能必须自己进行同步。
     */
    void setWakeupFn(Callback1 wakeup_fn, Object *ctx);

    /**
     * @brief enqueue
     * @param fn [in] 回调函数
     * @param fn_data [in] 传递给回调函数的参数, 禁止为空，对象包含是成员函数fn
     * @note 进入回调队列以在目标线程上异步运行它。目标线程一进入queue_process就会运行fn(fn_data)。
     *  注意，enqueue()通常会在这之前很久返回。这取决于用户是否发出回调完成的信号。
     *  也需要用户来保证上下文fn_data有正确的生存期，也就是直到回调函数运行，然后释放。
     */
    void enqueue(Callback1 fn, Object *fn_data);

    /**
     * @brief enqueueAutofree
     * @param fn [in] 回调函数
     * @param fn_data [in] 传递给回调函数的参数
     * @note 与enqueue()类似，但队列代码将在fn回调运行后
     *  会主动释放(fn_data)。###
     *  (回调本身可以简单地做到这一点，但它使使用相同的回调实现来实现同步和异步请求变得更容易。)
     */
    void enqueueAutofree(Callback1 fn, Object *fn_data);

    /**
     * @brief enqueueNotify
     * @param fn [in] 回调函数
     * @param fn_data [in] 传递给回调函数的参数
     * @note 与enqueue()类似，
     *  但是会合并相同的消息  ###
     */
    void enqueueNotify( Callback1 fn, Object *fn_data);

    /**
     * @brief cancelFn
     * @param fn [in] 回调函数
     * @param fn_data [in] 传递给回调函数的参数
     * @note 删除已经排队的项。只有加入了以下函数的条目才能被取消:
     *  - enqueue ()
     *  - enqueueNotify ()
     * 不能再取消已进入队列且当前正在执行的项目。
     * 这个函数主要用于从与queueProcess()相同的上下文中调用，其中可以排除“当前执行”的情况。
     */
    void cancelFn(Callback1 fn, Object *fn_data);

    /**
     * @brief run
     * @param fn [in] 回调函数
     * @param fn_data [in] 传递给回调函数的参数
     * @note 在目标线程上同步运行fn(fn_data)。这个函数将回调函数放入队列中并等待，直到目标线程完成此操作。
     *  这对于调用[un]lock()内部的函数来说是多余的，但对于依赖TLS的代码(如OpenGL)是有帮助的。
     *  在当前执行流程下，同步调用回调，fn也是在queueProcess线程中执行 ###
     */
    void run(Callback1 fn, Object *fn_data);

    /**
     * @brief queueProcess
     * @param timeout [in] 超时时间, 指定最小等待时间(单位：秒/s)；
     *  现在参数只是判断0和非0，0表示queue不进行超时等待；
     *  否则反之，超时时间获取通过timerHead队列中的定时节点。
     * @note 处理队列中任何未完成的分派项。这也可以通过lock()处理挂起或锁定另一个线程的This线程。
     *  timeout如果使用挂起/锁定函数，或者执行分派项需要时间，则在这个函数中花费的实际时间可能要高得多。
     *  另一方面，由于偶发的唤醒，这个函数可以返回比超时时间早得多的时间。
     *  注意，严格地说，只有在:
     *      —超时时间已过，
     *      -所有队列项都被处理，
     *      —可能获得的锁已经被释放
     *  可以通过调用interrupt()来取消超时。
     *  不允许重入调用。一次只能有1个线程调用queueProcess()。
     *  另外，lock()不能从调用queueProcess()的线程中调用(也就是说，没有进入队列的回调函数可以调用lock/unlock函数)。
     */
    void queueProcess(int timeout = -1);

    /**
     * @brief interrupt
     * @note 如果队列在queueProcess()中，则使它在所有工作项运行后立即返回，而不等待超时。如果它被 lock()阻塞，这并不会使它提前返回。
     * 如果队列在queueProcess()的_not_内，那么下次调用它时使用0的超时(如果您需要以无竞争的方式从另一个线程唤醒主线程，这是很有用的行为)。
     */
    void interrupt();

    /**
     * @brief adjustTimeout
     * @param until [in]
     * @note 如果queueProcess()调用正在进行，那么调整它因timeout参数而阻塞的最大时间。
     *  否则什么也不做。(在使用[un]lock()和普通事件循环的代码中调用这个函数是有意义的。)
     *  调用setWakeupFn()的队列不能正确工作，因为这意味着你实际上是通过queueProcess()来等待，
     *  而当你需要在外部api中等待时使用wakeup回调函数。
     * Not used.
     */
    void adjustTimeout(long until);

    /**
     * @brief lock
     * @note 授予对目标线程状态的独占访问权。当lock()激活时，没有其他线程可以从lock()返回
     *  (也就是说，它的行为类似于pthread互斥锁)，也没有其他线程可以完成调度项。
     *  其他线程仍然可以在不等待的情况下对异步调度项进行排队，互斥行为适用于这个函数，并且只适用于调度回调。
     *  锁是非递归的，可以认为调度回调函数已经持有调度锁。
     */
    void lock();

    //! 解锁
    void unlock();

    //! 是否正在执行回调函数
    bool isLocked();

    //! 调试打印
    void profile();

    //! 是否在同一个执行流程下
    bool isCommonFlow();

private:
    QueuePrivate *d;
    friend class Timer;
    friend class Loop;
};

/**
 * @brief The Timer class
 * @note 依托于queue队列的定时器处理
 */
class Timer : public Object
{
public:
    Timer(Queue *queue);
    ~Timer();

    /**
     * @brief setHandler
     * @param fn [in] 定时器执行的回调函数(ctx的成员函数)
     * @param ctx [in] 继承自Object类的子类对象，禁止为空
     * @note 定时器回调
     */
    void setHandler(Callback1 fn, Object *ctx);

    /**
     * @brief restart
     * @param msec [in] 定时器触发时间(单位：ms)
     * @note 启动定时器，相当于创建一个节点，添加进d->timerHead列表
     *  当定时时间到期时，会在queueProcess中得到执行
     */
    void restart(int msec);

    /**
     * @brief stop
     * @note 终止定时器，从d->timerHead列表中删除
     * 删除有两种情况：
     *  如果当前正在执行定时器，就只是把执行标志设置为false，queueProcess在执行完毕之后
     *   只要标志为false就会，里面回收这个定时节点
     *  定时器没有运行，立马从队列d->timerHead中回收
     */
    void stop();

    //! 运行标志，true：表示当前正在队列中，
    //! false：表示不会再执行这个定时器，在下一次检查确认时，从队列中删除
    bool isRunning() const;

    //! true：重复定时器，false：单步定时器
    bool repeat() const;
    void setRepeat(bool value);

    //! 重新设置触发时间
    int  mSec() const;
    void setMSec(int mSec);

private:
    QueuePrivate *d;
    bool    mIsRunning;  // 是否正在运行，内部操作，禁止外部对它赋值操作
    bool    mRepeat;     // 是否为连续间隔的定时器
    int     mMSec;       // 定时器触发时间
    Callback1 fn;
    Object    *ctx;

    friend class Queue;
    friend class QueuePrivate;
};

/**
 * @brief The EventLoop class
 * @note 依托于queue队列上的时间循环
 */
class Loop
{
public:
    class Evt
    {
    public:
        Evt() : sig(0), wParam(0), lParam(0), pHandler(nullptr)
        {}
        Evt(int sig, long w=0, long l=0, void *p=nullptr)
            : sig(sig), wParam(w), lParam(l), pHandler(p)
        {}

        int   sig;
        long  wParam;
        long  lParam;
        void *pHandler;
    };
    Loop();
    ~Loop();

    //! 必须设置
    void setQueue(Queue *queue);

    //! 子事件处理
    virtual void procSubMsg(const Evt &evt) = 0;

    //! 先进先出
    void post(const Evt &e);

    //! 后进先出
    void postFIFO(const Evt &e);

    //! 执行事件循环，在线程循环
    void exec();

    //! 处理事件循环,但是阻塞 msec毫秒,0表示不阻塞,1表示永久等待,>1 表示阻塞的时间
    void execOnce(int msec = 0);

    //! 退出事件循环
    void exit();

    //! 单步定时器执行
    void singleSlot(int msec, Object *ctx, Object::Callback1 fn);

    //! 单步定时器执行, 但是以事件的形式抛出
    void singleSlot(int msec, const Evt &evt);

protected:
    //! 中断事件循环，只能再procSubMsg中调用
    void loopBreak();

private:
     QueuePrivate *d;
     Queue        *q;
};

}

#endif // DISPATCH_PROCESS_H
